# 二进制整数

  • 原码,计算机中对数字的二进制定点表示方法,在数值前面增加了一位符号位(即最高位为符号位):正数该位为 0,负数该位为 1(0 有两种表示:+0 和-0),其余位表示数值的大小
  • 正数的补码和原码完全相同
  • 负数的补码是其反码加 1;反码是原码按位取反,但最高位(符号位)保持不变

# 常量和变量

# 常量

  1. 字面量常量 / 直接量
  2. final 定义的变量

不允许任何魔法值(即未经预先定义的常量)直接出现在代码中

# 直接量的类型

  • int 类型的直接量
    二进制(以 0B 或 0b 开头)、八进制(以 0 开头)、十进制、十六进制(以 0x 或 0X 开头),如 123、012(对应十进制的 10)、0x12(对应十进制的 18)
  • long 类型的直接量:在整型数值后添加 l 或 L,如 3L、0x12L
  • float 类型的直接量:在一个浮点数后添加 f 或 F,如 5.34F、3.14E5f
  • double 类型的直接量:标准小数形式或者科学计数法形式的浮点数,如 5.34、3.14E5
  • boolean 类型的直接量:只有 true 和 false
  • char 类型的直接量:单引号括起来的字符、转义字符和 Unicode 值表示的字符,如 'a'、'\n' 和 '\u0061'
  • String 类型的直接量:用双引号括起来的字符序列
  • null 类型的直接量:只有一个值,即 null,可以赋给任何引用类型的变量,用以表示这个引用类型变量中保存的地址为空,即还未指向任何有效对象

https://docs.oracle.com/javase/7/docs/technotes/guides/language/underscores-literals.html
从 Java 7 开始,可以在数字之间添加下划线,并且允许多个下划线,但不能在小数位、L/F 后缀或基数前缀旁边加下划线(如 123_ 3._14 110_L 0x_123 都会导致编译错误)

# 变量

  • 定义格式

    // 数据类型 变量名 = 初始化值; (声明、赋值)
    int num1 = 123;
    
    1
    2
  • 使用注意

    1. 同一作用域内同一变量不可重复声明
    2. 变量必须初始化后才能使用(即必须有值才能使用),否则编译报错
    3. 变量的值可在同一类型不断变化
    4. 变量命名符合标识符规范,使用驼峰命名法,首字母小写
    5. 就近原则

# 变量的分类及作用域

变量分类

不同数据类型的成员变量的初始值

  • 成员变量/全局变量/字段:直接定义在类中方法外的变量

    1. 类变量 使用 static 修饰的字段 方法区
    2. 实例变量 没有使用 static 修饰的字段
    • 默认是有初始值的,可以先在方法中使用后定义
    • 作用域是整个类
  • 局部变量

    1. 方法的形参;2. 方法内中的变量;3.代码块中的变量
    • 没有初始值,必须显式初始化后才能使用
    • 定义局部变量后,系统并未分配内存空间,直到程序为这个变量赋值时,系统才会在所在方法的的栈内存中为局部变量分配内存,并将初始值(基本类型的值或者对象的引用)保存在该内存中
    • 定义的位置 开始到所在结束的花括号

# 数据类型的分类

数据类型分类

基本数据类型范围

基本类型

# boolean 类型

  • 常量值只能是 true 或 false
boolean isMan = true;
1

# 整数类型

  • byte, short, int, long
  1. 二进制(补码) 以 0B 或 0b 开头,0B0110
  2. 八进制 以 0 开头,056
  3. 十进制 17
  4. 十六进制 以 0X 或 0x 开头,0XFF
int i = 110; // 整数常量默认是 int 类型
long l = 110L; // long 类型常量需在整数后添加 l 或 L 后缀
long l = 110; // 可以把一个较小的整数值(在 int 类型的表数范围以内)直接赋给一个 long 类型的变量(自动类型转换)
1
2
3

# 浮点数类型

  • float, double
double pi = 3.14;  // 浮点数常量默认是 double 类型
float  pi = 3.14F; // float 类型常量需在浮点数后添加 f 或 F 后缀
float  pi = 3.14; // 错误: 不兼容的类型: 从 double 转换到 float 可能会有损失
3.14 == 3.14F; // false
3.14 == 3.14D; // true
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
a == b; // false
1
2
3
4
5
6
7
8
  • 注意:浮点数采用“尾数+阶码”的编码方式(IEEE 754 标准),所以 float, double 都不能表示精确的浮点数,需用 BigDecimal 类型

# 字符类型

  • char 16 位 Unicode 字符集,编码方式为 UTF-16BE
  • UTF-16 使用 2 或者 4 字节表示一个字符,在 65536 以内的占两个字节,而基本上所有汉字的 Unicode 编码在 19968 到 40869 之间 (opens new window),所以一个 char 类型可以存储一个汉字
  • 使用''括起来
    • 49 '1'; 65 'A'; 97 'a'
// 表示形式
char c1 = 'A'; // 使用单个字符
char c2 = 65; // 使用十进制的整数(Unicode 值),[0, 65535]
char c3 = '\u0061'; // 使用十六进制的整数,格式'\uXXXX',('\u0000'~'\u00FF')
1
2
3
4

# 最常用的引用类型 String

  • 字符串常量使用""引起来的,连接字符串使用 “+” 符号
  • 字符串拼接:字符串可以和任何基本类型的值或 Java 对象进行连接运算,结果都是字符串类型(基本类型的值将自动类型转换为字符串类型,系统自动调用 Java 对象 toString() 方法)

# 基本类型的类型转换

基本数据类型转换

  • boolean 不属于数值类型,不参与转换

# 自动类型转换 / 隐式类型转换

  • 如果直接将一个较小的整数常量(在 byte 或 short 类型的表数范围内)赋给一个 byte 或 short 变量,系统会自动把这个整型常量当成 byte 或 short 类型来处理

# 强制类型转换

  • 语法格式:(targetType)value
byte b2 = 65; // 系统会自动把 65 当成 byte 类型处理
int a = 12;
byte b = (byte) a; // 强制类型转换
float a = (float) 5.6; // 强制类型转换
int i = (int) -12.81; // 强制类型转换(小数部分被截掉),i = -12
(char) 65 // 表示 'A' 字符
1
2
3
4
5
6

# 表达式类型的自动提升

  • 所有的 byteshortchar 类型被自动提升到 int 类型
  • 整个算术表达式最终结果的数据类型被提升到表达式中操作数类型最高的类型
short s = 5; // 自动类型转换(隐式类型转换)
s = s - 2; // 错误: 不兼容的类型: 从int转换到short可能会有损失 
1
2

# 运算符

# 算术运算符

# 加号 +

  • 加号在操作数值、字符、字符串时,其结果是不同的
    1. 当两个字符相加得到的是 ASCII 码表值
    2. 作为字符串连接运算符

# 除号 /

  • 如果除法运算符的两个操作数都是整数类型,则计算结果也是整数(截断小数部分取整),此时除数不能是 0,否则将引发除以零异常 ArithmeticException: / by zero
  • 如果除法运算符的两个操作数有 1 个是浮点数,或者 2 个都是浮点数,则计算结果也是浮点数,此时允许除数是 0,或者 0.0,得到结果是正无穷大或负无穷大

# 取模(求余数)%

  • 被模数 % 模数
  • 模数的符号忽略不计,结果的正负取决于被模数
  • 如果求余运算的两个操作数都是整数类型,则求余运算的第二个运算数不能是 0,否则将引发除以零异常
'A' + 'B'  // 131
"A" + "B"  // AB

10 / 3  // 3

System.out.println(10 / 0) // ArithmeticException: / by zero
System.out.println(10.0 / 0) // 输出正无穷大:Infinity 
System.out.println(-10.0 / 0) // 输出负无穷大:-Infinity 
System.out.println(0 / 0.0) // 输出非数(Not a Number):NaN

/*
注意:
无穷大和 NaN 都属于 double 浮点类型
但是所有正无穷大数值都是相等的,所有负无穷大数值也是相等的
NaN 不与任何数值相等,也不等于自己
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 自增 ++ 自减 --

  • 自增、自减操作都是直接修改变量的值(读、改、写),不经过操作数栈
  • 前置 ( ++i ):局部变量表中的 i 先自增,再把 i 的值压入操作数栈
  • 后置 ( i++ ):先把局部变量表中的 i 压入操作数栈,再自增
int i = 1;
i = ++i;
System.out.println(i); // 2

int j = 1;
j = j++;
System.out.println(j); // 1
1
2
3
4
5
6
7

# 赋值运算符

  • += 隐式的将加操作的结果类型强制转换为持有结果的类型
byte a = 127;
byte b = 127;
b = a + b; // error : cannot convert from int to byte
b += a; // ok

short s = 5;
s += 5; // s = (short)(s + 5)
1
2
3
4
5
6
7

# 比较运算符

  • 比较运算符的结果是 boolean 类型

  • == != < > <= >= instanceof

  • 使用 == 判断两个变量是否相等

    1. 基本类型变量:只要两个变量的值相等( 不一定要求数据类型严格相同),就返回 true
    2. 引用类型变量:只有两个变量指向同一个对象时,返回 true(不可用于比较类型上没有继承关系的两个对象,否则编译报错)
97 == 'a'; // true
5.0 == 5; // true
"hello" == new Animal(); // 编译报错
1
2
3

# 三元运算符 / 三目运算符

  • boolean 表达式 ? 表达式 1 : 表达式 2
  • 一个三元表达式不会既计算 表达式 1,又计算 表达式 2
// 判断奇数偶数
String ret = number % 2 == 0 ? "偶数" : "奇数";
1
2

https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25

三目运算符 condition ? 表达式 1 : 表达式 2 中,需要高度注意表达式 1 和 2 在类型对齐时,可能抛出因自动拆箱导致的 NPE 异常,以下两种场景会触发类型对齐拆箱操作:

  1. 表达式 1 或表达式 2 的值只要有一个原始类型,此时三目运算符的运算结果为原始类型
  2. 表达式 1 或表达式 2 的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型对应的原始类型

# 逻辑运算符

  • 用于操作两个 boolean 类型的变量或常量,结果也是 boolean 类型
  • & :与,都为 true,结果才为 true,否则结果是 false
  • && :短路与,如果左边的操作数是 false,结果一定为 false,且不再计算右边的操作数
  • | :或,都为 false,结果才为 false,只要有一个是 true,结果就是 true
  • || :短路或,如果左边的操作数是 true,结果一定为 true,且不再计算右边的操作数
  • ^ :异或,判断两个操作数是否不同,不同则为 true,相同则为 false
  • ! :取反,!true 结果是 false,!fasle 结果是 true

# 位运算符

  • 操作的是补码的二进制位
  • 位运算符只能操作整数类型的变量或值
  • & :按位与,当两位同时为 1 时才返回 1
  • | :按位或,只要有一位为 1 即可返回 1
  • ~ :按位非,单目运算符,将操作数的每个位(包括符号位)全部取反
  • ^ :按位异或,当两位相同时返回 0,不同时返回 1
  • << :左移运算符,左移后右边位补 0
  • >> :右移运算符,右移后左边位补原最左位值(可能是 0,可能是 1)
  • >>>:无符号右移运算符,右移后左边位补 0
// 左移 k 位相当于乘以 2 的 k 次方
// 右移 k 位相当于除以 2 的 k 次方
1 << 10; // 等于 2 的 10 次方
3 >> 1; // 效率高的除以 2,等价于 3 / 2

n & 1; // 等价于 n % 2,判断奇偶性(偶数的二进制末位为 0,奇数的二进制末位为 1)
1
2
3
4
5
6

n * 2k = n << k

n / 2k = n >> k

正整数 n 对 2 的 k 次方取模,n % 2k = n & (2k - 1) = n & ((1 << k) - 1)

// 交换两个变量的值
int a = 10;
int b = 12;

// 第一种方法,使用临时变量
int temp = a;
a = b;
b = temp;

// 第二种方法
// 把 a、b 看做数轴上的点,围绕两点间的距离来进行计算(可能会越界)
a = b - a;
b = b - a;
a = b + a;

// 第三种方法
// 任意一个数与任意一个给定的值连续异或两次,值不变
a = a ^ b;
b = a ^ b;
a = a ^ b;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  • 测试第 k 位:s & (1 << k)
  • 设置第 k 位:s |= (1 << k)
  • 第 k 位置零:s &= ~(1 << k)
  • 切换第 k 位值:s ^= ~(1 << k)
  • 乘以 2ns << n
  • 除以 2ns >> n
  • 交集:s & t
  • 并集:s | t
  • 减法:s & ~t
  • 交换: x = x ^ y ^ (y = x)
  • 取出最小非 0 位(Extract lowest set bit):s & (-s)
  • 取出最小 0 位(Extract lowest unset bit):~s & (s + 1)
  • 交换值:x ^= y; y ^= x; x ^= y;

# 表达式

  • 表达式不是完整的语句!
Updated at: 2023-12-12 09:24:20